/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.openatom.ubml.model.common.definition.commonmodel.json.object;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.ArrayList;
import org.openatom.ubml.model.common.definition.cef.IGspCommonField;
import org.openatom.ubml.model.common.definition.cef.collection.BaseList;
import org.openatom.ubml.model.common.definition.cef.collection.GspAssociationKeyCollection;
import org.openatom.ubml.model.common.definition.cef.collection.GspFieldCollection;
import org.openatom.ubml.model.common.definition.cef.entity.GspCommonDataType;
import org.openatom.ubml.model.common.definition.cef.entity.MdRefInfo;
import org.openatom.ubml.model.common.definition.cef.json.CefNames;
import org.openatom.ubml.model.common.definition.cef.json.SerializerUtils;
import org.openatom.ubml.model.common.definition.cef.json.element.CefFieldDeserializer;
import org.openatom.ubml.model.common.definition.cef.json.element.GspAssoKeyDeserializer;
import org.openatom.ubml.model.common.definition.cef.json.object.GspCommonDataTypeDeserializer;
import org.openatom.ubml.model.common.definition.commonmodel.collection.GspElementCollection;
import org.openatom.ubml.model.common.definition.commonmodel.collection.GspObjectCollection;
import org.openatom.ubml.model.common.definition.commonmodel.collection.GspUniqueConstraintCollection;
import org.openatom.ubml.model.common.definition.commonmodel.entity.GspCommonObject;
import org.openatom.ubml.model.common.definition.commonmodel.entity.object.ConstraintType;
import org.openatom.ubml.model.common.definition.commonmodel.entity.object.GspColumnGenerate;
import org.openatom.ubml.model.common.definition.commonmodel.entity.object.GspCommonObjectType;
import org.openatom.ubml.model.common.definition.commonmodel.entity.object.GspUniqueConstraint;
import org.openatom.ubml.model.common.definition.commonmodel.json.CommonModelNames;
import org.openatom.ubml.model.common.definition.commonmodel.json.element.CmElementDeserializer;

import static com.fasterxml.jackson.core.JsonToken.END_ARRAY;
import static com.fasterxml.jackson.core.JsonToken.FIELD_NAME;

/**
 * The Json Deserializer Of Common Model Object
 *
 * @ClassName: CmObjectDeserializer
 * @Author: Benjamin Gong
 * @Date: 2021/1/11 17:13
 * @Version: V1.0
 */
public abstract class CmObjectDeserializer extends GspCommonDataTypeDeserializer {

    @Override
    protected final boolean readExtendDataTypeProperty(GspCommonDataType commonDataType, String propName, JsonParser jsonParser) {
        boolean result = true;
        GspCommonObject object = (GspCommonObject)commonDataType;
        switch (propName) {
            case CommonModelNames.REPOSITORY_COMPS:
                ArrayList<MdRefInfo> infos = new ArrayList<>();
                try {
//                  SerializerUtils.readStartArray(jsonParser);
                    ObjectMapper mapper = new ObjectMapper();
                    JavaType type = mapper.getTypeFactory().
                            constructCollectionType(ArrayList.class, MdRefInfo.class);
                    infos = mapper.readValue(jsonParser, type);
                    jsonParser.nextToken();
                } catch (IOException e) {
                    throw new RuntimeException(String.format("CmObjectDeserializer反序列化错误：%1$s", propName));
                }
                object.setRepositoryComps(infos);
                break;
            case CommonModelNames.OBJECT_TYPE:
                object.setObjectType(SerializerUtils.readPropertyValue_Enum(jsonParser, GspCommonObjectType.class, GspCommonObjectType.values()));
                break;
            case CommonModelNames.REF_OBJECT_NAME:
                object.setRefObjectName(SerializerUtils.readPropertyValue_String(jsonParser));
                break;
            case CommonModelNames.LOGIC_DELETE:
                SerializerUtils.readPropertyValue_boolean(jsonParser);
                break;
            case CommonModelNames.GENERATE_ID:
                object.setColumnGenerateID(readColumnGenerateId(jsonParser));
                break;
            case CommonModelNames.CONTAIN_CHILD_OBJECTS:
                readContainChildObjects(jsonParser, object);
                break;
            case CommonModelNames.CONTAIN_CONSTRAINTS:
                readContainConstraints(jsonParser, object);
                break;
            case CommonModelNames.ORDERBY_CONDITION:
                object.setOrderbyCondition(SerializerUtils.readPropertyValue_String(jsonParser));
                break;
            case CommonModelNames.FILTER_CONDITION:
                object.setFilterCondition(SerializerUtils.readPropertyValue_String(jsonParser));
                break;
            case CommonModelNames.MODIFIED_DATE_ELEMENT_ID:
            case CommonModelNames.CREATOR_ELEMENT_ID:
            case CommonModelNames.MODIFIER_ELEMENT_ID:
            case CommonModelNames.DATE_ELEMENT_ID:
            case CommonModelNames.STATE_ELEMENT_ID:
                SerializerUtils.readPropertyValue_String(jsonParser);
                break;
            case CommonModelNames.RECORD_DEL_DATA:
                SerializerUtils.readPropertyValue_boolean(jsonParser);
                break;
            case CommonModelNames.IS_READ_ONLY:
                object.setIsReadOnly(SerializerUtils.readPropertyValue_boolean(jsonParser));
                break;
            case CommonModelNames.IS_VIRTUAL:
                object.setIsVirtual(SerializerUtils.readPropertyValue_boolean(jsonParser));
                break;
            case CommonModelNames.HIRARCHY_INFO:
                readHirarchyInfo(jsonParser);
                break;
            case CommonModelNames.KEYS:
                readKeys(jsonParser, object);
                break;
            case "ParentObjectID":
                SerializerUtils.readPropertyValue_String(jsonParser);
                break;
            case CommonModelNames.BELONG_MODEL_ID:
                object.setBelongModelID(SerializerUtils.readPropertyValue_String(jsonParser));
                break;

            default:
                result = ReadExtendObjectProperty(object, propName, jsonParser);
                break;
        }
        return result;
    }

    protected void readKeys(JsonParser jsonParser, GspCommonObject object) {
        GspAssociationKeyCollection collection = new GspAssociationKeyCollection();
        GspAssoKeyDeserializer deserializer = new GspAssoKeyDeserializer();
        SerializerUtils.readArray(jsonParser, deserializer, collection);
        object.setKeys(collection);
    }

    //hirachyInfo无用，已删除
    private void readHirarchyInfo(JsonParser jsonParser) {
        JsonDeserializer<GspUniqueConstraint> deserializer = new JsonDeserializer<GspUniqueConstraint>() {
            @Override
            public GspUniqueConstraint deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
                GspUniqueConstraint hirarchyInfo = new GspUniqueConstraint();

                SerializerUtils.readStartObject(jsonParser);
                while (jsonParser.getCurrentToken() == FIELD_NAME) {
                    String propName = SerializerUtils.readPropertyName(jsonParser);
                    switch (propName) {
                        case CommonModelNames.DETAIL_ELEMENT:
                            SerializerUtils.readPropertyValue_String(jsonParser);
                            break;
                        case CommonModelNames.LAYER_ELEMENT:
                            SerializerUtils.readPropertyValue_String(jsonParser);
                            break;
                        case CommonModelNames.PARENT_ELEMENT:
                            SerializerUtils.readPropertyValue_String(jsonParser);
                            break;
                        case CommonModelNames.PARENT_REF_ELEMENT:
                            SerializerUtils.readPropertyValue_String(jsonParser);
                            break;
                        case CommonModelNames.PATH_ELEMENT:
                            SerializerUtils.readPropertyValue_String(jsonParser);
                            break;
                        case CommonModelNames.PATH_GENERATE_TYPE:
                            SerializerUtils.readPropertyValue_Integer(jsonParser);
                            break;
                        case CommonModelNames.PATH_LENGTH:
                            SerializerUtils.readPropertyValue_Integer(jsonParser);
                            break;
                    }
                }
                SerializerUtils.readEndObject(jsonParser);
                return null;
            }
        };
        try {
            deserializer.deserialize(jsonParser, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void readContainConstraints(JsonParser jsonParser, GspCommonObject object) {
        GspUniqueConstraintCollection collection = new GspUniqueConstraintCollection();
        JsonDeserializer<GspUniqueConstraint> deserializer = new JsonDeserializer<GspUniqueConstraint>() {
            @Override
            public GspUniqueConstraint deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
                GspUniqueConstraint value = new GspUniqueConstraint();

                SerializerUtils.readStartObject(jsonParser);
                while (jsonParser.getCurrentToken() == FIELD_NAME) {
                    String propName = SerializerUtils.readPropertyName(jsonParser);
                    switch (propName) {
                        case CommonModelNames.ID:
                            value.setId(SerializerUtils.readPropertyValue_String(jsonParser));
                            break;
                        case CommonModelNames.CODE:
                            value.setCode(SerializerUtils.readPropertyValue_String(jsonParser));
                            break;
                        case CommonModelNames.NAME:
                            value.setName(SerializerUtils.readPropertyValue_String(jsonParser));
                            break;
                        case CommonModelNames.TYPE:
                            value.setType(SerializerUtils.readPropertyValue_Enum(jsonParser, ConstraintType.class, ConstraintType.values()));
                            break;
                        case CommonModelNames.CONSTRAINT_MESSAGE:
                            value.setConstraintMessage(SerializerUtils.readPropertyValue_String(jsonParser));
                            break;
                        case CommonModelNames.ELEMENT_LIST:
                            GspElementCollection collection = new GspElementCollection(getGspCommonDataType());
                            CefFieldDeserializer deserializer = createFieldDeserializer();
                            SerializerUtils.readArray(jsonParser, deserializer, collection);
                            BaseList<String> list = new BaseList<String>();
                            for (IGspCommonField field : collection) {
                                list.add(field.getID());
                            }
                            value.setElementList(list);
                            break;
                        case CefNames.I18N_RESOURCE_INFO_PREFIX:
                            value.setI18nResourceInfoPrefix(SerializerUtils.readPropertyValue_String(jsonParser));
                            break;

                    }
                }
                SerializerUtils.readEndObject(jsonParser);
                return value;
            }
        };
        SerializerUtils.readArray(jsonParser, deserializer, collection);
        object.setContainConstraints(collection);
    }

    private void readContainChildObjects(JsonParser parser, GspCommonObject object) {
        GspObjectCollection collection = createChildObjectCollection();
        CmObjectDeserializer deserializer = createCmObjectDeserializer();
        if (SerializerUtils.readNullObject(parser)) {
            return;
        }
        SerializerUtils.readStartArray(parser);
        JsonToken tokentype = parser.getCurrentToken();
        if (tokentype != END_ARRAY) {
            while (parser.getCurrentToken() == tokentype) {
                GspCommonObject item = (GspCommonObject)deserializer.deserializeCommonDataType(parser);
                collection.add(item);
            }
        }
        SerializerUtils.readEndArray(parser);
        object.setContainChildObjects(collection);
    }

    protected abstract CmObjectDeserializer createCmObjectDeserializer();

    private GspColumnGenerate readColumnGenerateId(JsonParser jsonParser) {
        GspColumnGenerate columnGenerate = new GspColumnGenerate();
        if (SerializerUtils.readNullObject(jsonParser)) {
            return columnGenerate;
        }
        SerializerUtils.readStartObject(jsonParser);
        while (jsonParser.getCurrentToken() == FIELD_NAME) {
            String propName = SerializerUtils.readPropertyName(jsonParser);
            readColumnGenerateIdPropValue(jsonParser, propName, columnGenerate);
        }
        SerializerUtils.readEndObject(jsonParser);
        return columnGenerate;
    }

    private void readColumnGenerateIdPropValue(JsonParser jsonParser, String propName, GspColumnGenerate columnGenerate) {
        switch (propName) {
            case CommonModelNames.ELEMENT_ID:
                columnGenerate.setElementID(SerializerUtils.readPropertyValue_String(jsonParser));
                break;
            case CommonModelNames.GERNERATE_TYPE:
            case CommonModelNames.GENERATE_TYPE:
                columnGenerate.setGenerateType(SerializerUtils.readPropertyValue_String(jsonParser));
                break;
            default:
                throw new RuntimeException(String.format("GspCommonDataTypeDeserializer.readColumnGenerateIdPropValue未识别的属性名：%1$s", propName));
        }
    }

    protected boolean ReadExtendObjectProperty(GspCommonObject object, String propName, JsonParser jsonParser) {
        return false;
    }

    @Override
    protected final GspCommonDataType createCommonDataType() {
        return CreateCommonObject();
    }

    @Override
    protected final CefFieldDeserializer createFieldDeserializer() {
        return CreateElementDeserializer();
    }

    @Override
    protected final GspFieldCollection createFieldCollection() {
        return createCommonElementCollection();
    }

    protected GspElementCollection createCommonElementCollection() {
        return new GspElementCollection(getGspCommonDataType());
    }

    protected GspObjectCollection createChildObjectCollection() {
        return new GspObjectCollection(getGspCommonDataType());
    }

    @Override
    protected final GspCommonObject getGspCommonDataType() {
        return (GspCommonObject)super.getGspCommonDataType();
    }

    protected abstract GspCommonObject CreateCommonObject();

    protected abstract CmElementDeserializer CreateElementDeserializer();
}
